﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;

namespace LaughingDog.Input
{
    /// <summary>
    /// Enumeration of all XBox 360 GamePad buttons.
    /// </summary>
    public enum GamePadButtons { A, B, X, Y, BigButton, Back, LeftShoulder, RightShoulder, Start, LeftStick, RightStick, DPadUp, DPadDown, DPadLeft, DPadRight}

    /// <summary>
    /// Enumeration of all XBox 360 GamePad button event states.
    /// </summary>
    public enum GamePadButtonStates { Released, JustReleased, Pressed, JustPressed }
    
    public class GamePad : InputDevice
    {
        private PlayerIndex mPlayerIndex;
        private GamePadState mCurrentState = new GamePadState();
        private GamePadState mPreviousState = new GamePadState();
        private Dictionary<GamePadButtons, GamePadButtonStates> mButtonStates = new Dictionary<GamePadButtons, GamePadButtonStates>();        

        private ThumbStick mLeftStick = new ThumbStick();
        private ThumbStick mRightStick = new ThumbStick();

        public Boolean IsConnected { get { return mCurrentState.IsConnected; } }
        public ThumbStick LeftStick { get { return mLeftStick; } }
        public ThumbStick RightStick { get { return mRightStick; } }

        public Single LeftTrigger { get { return mCurrentState.Triggers.Left; } }
        public Single RightTrigger { get { return mCurrentState.Triggers.Right; } }

        public PlayerIndex PlayerIndex { get { return mPlayerIndex; } }

        /// <summary>
        /// Creates a new instance of a GamePad device. One is automatically created in the InputManager and can be accessed via InputManager.Instance.GamePads.
        /// </summary>
        public GamePad(PlayerIndex playerIndex)
        {
            mPlayerIndex = playerIndex;
            
            foreach (GamePadButtons button in Enum.GetValues(typeof(GamePadButtons)))
                mButtonStates.Add(button, GamePadButtonStates.Released);            
        }

        /// <summary>
        /// Updates this GamePad device.
        /// </summary>
        public override void Update()
        {
            mCurrentState = Microsoft.Xna.Framework.Input.GamePad.GetState(mPlayerIndex);
            
            #region A Button

            if (mCurrentState.Buttons.A == ButtonState.Pressed
                && mPreviousState.Buttons.A == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.A] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.Buttons.A == ButtonState.Pressed
                && mPreviousState.Buttons.A == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.A] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.Buttons.A == ButtonState.Released
                && mPreviousState.Buttons.A == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.A] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.Buttons.A == ButtonState.Released
                && mPreviousState.Buttons.A == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.A] = GamePadButtonStates.Released;
            }

            #endregion

            #region B Button

            if (mCurrentState.Buttons.B == ButtonState.Pressed
                && mPreviousState.Buttons.B == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.B] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.Buttons.B == ButtonState.Pressed
                && mPreviousState.Buttons.B == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.B] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.Buttons.B == ButtonState.Released
                && mPreviousState.Buttons.B == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.B] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.Buttons.B == ButtonState.Released
                && mPreviousState.Buttons.B == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.B] = GamePadButtonStates.Released;
            }

            #endregion

            #region X Button

            if (mCurrentState.Buttons.X == ButtonState.Pressed
                && mPreviousState.Buttons.X == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.X] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.Buttons.X == ButtonState.Pressed
                && mPreviousState.Buttons.X == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.X] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.Buttons.X == ButtonState.Released
                && mPreviousState.Buttons.X == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.X] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.Buttons.X == ButtonState.Released
                && mPreviousState.Buttons.X == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.X] = GamePadButtonStates.Released;
            }

            #endregion

            #region Y Button

            if (mCurrentState.Buttons.Y == ButtonState.Pressed
                && mPreviousState.Buttons.Y == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.Y] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.Buttons.Y == ButtonState.Pressed
                && mPreviousState.Buttons.Y == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.Y] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.Buttons.Y == ButtonState.Released
                && mPreviousState.Buttons.Y == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.Y] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.Buttons.Y == ButtonState.Released
                && mPreviousState.Buttons.Y == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.Y] = GamePadButtonStates.Released;
            }

            #endregion

            #region Back Button

            if (mCurrentState.Buttons.Back == ButtonState.Pressed
                && mPreviousState.Buttons.Back == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.Back] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.Buttons.Back == ButtonState.Pressed
                && mPreviousState.Buttons.Back == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.Back] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.Buttons.Back == ButtonState.Released
                && mPreviousState.Buttons.Back == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.Back] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.Buttons.Back == ButtonState.Released
                && mPreviousState.Buttons.Back == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.Back] = GamePadButtonStates.Released;
            }

            #endregion

            #region Big Button

            if (mCurrentState.Buttons.BigButton == ButtonState.Pressed
                && mPreviousState.Buttons.BigButton == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.BigButton] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.Buttons.BigButton == ButtonState.Pressed
                && mPreviousState.Buttons.BigButton == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.BigButton] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.Buttons.BigButton == ButtonState.Released
                && mPreviousState.Buttons.BigButton == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.BigButton] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.Buttons.BigButton == ButtonState.Released
                && mPreviousState.Buttons.BigButton == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.BigButton] = GamePadButtonStates.Released;
            }

            #endregion

            #region Left Shoulder Button

            if (mCurrentState.Buttons.LeftShoulder == ButtonState.Pressed
                && mPreviousState.Buttons.LeftShoulder == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.LeftShoulder] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.Buttons.LeftShoulder == ButtonState.Pressed
                && mPreviousState.Buttons.LeftShoulder == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.LeftShoulder] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.Buttons.LeftShoulder == ButtonState.Released
                && mPreviousState.Buttons.LeftShoulder == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.LeftShoulder] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.Buttons.LeftShoulder == ButtonState.Released
                && mPreviousState.Buttons.LeftShoulder == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.LeftShoulder] = GamePadButtonStates.Released;
            }

            #endregion

            #region Right Shoulder Button

            if (mCurrentState.Buttons.RightShoulder == ButtonState.Pressed
                && mPreviousState.Buttons.RightShoulder == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.RightShoulder] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.Buttons.RightShoulder == ButtonState.Pressed
                && mPreviousState.Buttons.RightShoulder == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.RightShoulder] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.Buttons.RightShoulder == ButtonState.Released
                && mPreviousState.Buttons.RightShoulder == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.RightShoulder] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.Buttons.RightShoulder == ButtonState.Released
                && mPreviousState.Buttons.RightShoulder == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.RightShoulder] = GamePadButtonStates.Released;
            }

            #endregion

            #region Left Stick Button

            if (mCurrentState.Buttons.LeftStick == ButtonState.Pressed
                && mPreviousState.Buttons.LeftStick == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.LeftStick] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.Buttons.LeftStick == ButtonState.Pressed
                && mPreviousState.Buttons.LeftStick == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.LeftStick] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.Buttons.LeftStick == ButtonState.Released
                && mPreviousState.Buttons.LeftStick == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.LeftStick] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.Buttons.LeftStick == ButtonState.Released
                && mPreviousState.Buttons.LeftStick == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.LeftStick] = GamePadButtonStates.Released;
            }

            #endregion

            #region Right Stick Button

            if (mCurrentState.Buttons.RightStick == ButtonState.Pressed
                && mPreviousState.Buttons.RightStick == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.RightStick] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.Buttons.RightStick == ButtonState.Pressed
                && mPreviousState.Buttons.RightStick == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.RightStick] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.Buttons.RightStick == ButtonState.Released
                && mPreviousState.Buttons.RightStick == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.RightStick] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.Buttons.RightStick == ButtonState.Released
                && mPreviousState.Buttons.RightStick == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.RightStick] = GamePadButtonStates.Released;
            }

            #endregion

            #region Start Button

            if (mCurrentState.Buttons.Start == ButtonState.Pressed
                && mPreviousState.Buttons.Start == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.Start] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.Buttons.Start == ButtonState.Pressed
                && mPreviousState.Buttons.Start == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.Start] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.Buttons.Start == ButtonState.Released
                && mPreviousState.Buttons.Start == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.Start] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.Buttons.Start == ButtonState.Released
                && mPreviousState.Buttons.Start == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.Start] = GamePadButtonStates.Released;
            }

            #endregion

            #region DPad Up Button

            if (mCurrentState.DPad.Up == ButtonState.Pressed
                && mPreviousState.DPad.Up == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.DPadUp] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.DPad.Up == ButtonState.Pressed
                && mPreviousState.DPad.Up == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.DPadUp] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.DPad.Up == ButtonState.Released
                && mPreviousState.DPad.Up == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.DPadUp] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.DPad.Up == ButtonState.Released
                && mPreviousState.DPad.Up == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.DPadUp] = GamePadButtonStates.Released;
            }

            #endregion

            #region DPad Down  Button

            if (mCurrentState.DPad.Down == ButtonState.Pressed
                && mPreviousState.DPad.Down == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.DPadDown] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.DPad.Down == ButtonState.Pressed
                && mPreviousState.DPad.Down == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.DPadDown] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.DPad.Down == ButtonState.Released
                && mPreviousState.DPad.Down == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.DPadDown] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.DPad.Down == ButtonState.Released
                && mPreviousState.DPad.Down == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.DPadDown] = GamePadButtonStates.Released;
            }

            #endregion

            #region DPad Left Button

            if (mCurrentState.DPad.Left == ButtonState.Pressed
                && mPreviousState.DPad.Left == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.DPadLeft] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.DPad.Left == ButtonState.Pressed
                && mPreviousState.DPad.Left == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.DPadLeft] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.DPad.Left == ButtonState.Released
                && mPreviousState.DPad.Left == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.DPadLeft] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.DPad.Left == ButtonState.Released
                && mPreviousState.DPad.Left == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.DPadLeft] = GamePadButtonStates.Released;
            }

            #endregion

            #region DPad Right Button

            if (mCurrentState.DPad.Right == ButtonState.Pressed
                && mPreviousState.DPad.Right == ButtonState.Released) // Down this frame, not the last
            {
                mButtonStates[GamePadButtons.DPadRight] = GamePadButtonStates.JustPressed;
            }
            else if (mCurrentState.DPad.Right == ButtonState.Pressed
                && mPreviousState.DPad.Right == ButtonState.Pressed) // Down this frame and the last
            {
                mButtonStates[GamePadButtons.DPadRight] = GamePadButtonStates.Pressed;
            }
            else if (mCurrentState.DPad.Right == ButtonState.Released
                && mPreviousState.DPad.Right == ButtonState.Pressed) // Up this frame, not the last
            {
                mButtonStates[GamePadButtons.DPadRight] = GamePadButtonStates.JustReleased;
            }
            else if (mCurrentState.DPad.Right == ButtonState.Released
                && mPreviousState.DPad.Right == ButtonState.Released) // Up this frame and the last
            {
                mButtonStates[GamePadButtons.DPadRight] = GamePadButtonStates.Released;
            }

            #endregion

            mLeftStick.Update(mCurrentState);
            mRightStick.Update(mCurrentState);
            
            mPreviousState = mCurrentState;
        }

        /// <summary>
        /// Returns whether a GamePad button is currently pressed. Down last frame and this one.
        /// </summary>
        /// <param name="button">The GamePad button to check.</param>
        /// <returns>True if the button is currenty pressed, false if not.</returns>
        public Boolean ButtonPressed(GamePadButtons button)
        {
            return IsButtonInState(button, GamePadButtonStates.Pressed);
        }

        /// <summary>
        /// Returns whether a GamePad button has just been pressed. Down this frame but up during last one.
        /// </summary>
        /// <param name="button">The GamePad button to check.</param>
        /// <returns>True if the button was just pressed, false if not.</returns>
        public Boolean ButtonJustPressed(GamePadButtons button)
        {
            return IsButtonInState(button, GamePadButtonStates.JustPressed);
        }

        /// <summary>
        /// Returns whether a GamePad button has just been released. Up this frame but down during last one.
        /// </summary>
        /// <param name="button">The GamePad button to check.</param>
        /// <returns>True if the button was just released, false if not.</returns>
        public Boolean ButtonJustReleased(GamePadButtons button)
        {
            return IsButtonInState(button, GamePadButtonStates.JustReleased);
        }

        /// <summary>
        /// Returns whether a GamePad button is currently released. Up last frame and this one.
        /// </summary>
        /// <param name="button">The GamePad button to check.</param>
        /// <returns>True if the button is currently released, false if not.</returns>
        public Boolean ButtonReleased(GamePadButtons button)
        {
            return IsButtonInState(button, GamePadButtonStates.Released);
        }

        /// <summary>
        /// Checks if a GamePad button is in a certain state.
        /// </summary>
        /// <param name="button">The GamePad button to check.</param>
        /// <param name="state">The state to check.</param>
        /// <returns>True if the button is in the specified state, false if not.</returns>
        public Boolean IsButtonInState(GamePadButtons button, GamePadButtonStates state)
        {
            return mButtonStates[button] == state;
        }

        /// <summary>
        /// Gets the state of a certain button.
        /// </summary>
        /// <param name="button">The button to check.</param>
        /// <returns>The state the button is in.</returns>
        public GamePadButtonStates GetButtonState(GamePadButtons button)
        {
            return mButtonStates[button];
        }

        /// <summary>
        /// Sets the vibration of the GamePad. Values should be between 0.0f and 1.0f.
        /// </summary>
        /// <param name="leftMotor">The value for the low frequency motor.</param>
        /// <param name="rightMotor">The value for the high frequency motor.</param>
        public void SetVibration(Single leftMotor, Single rightMotor)
        {
            Microsoft.Xna.Framework.Input.GamePad.SetVibration(mPlayerIndex, leftMotor, rightMotor);
        }

        /// <summary>
        /// Sets the vibration of the GamePad. Values should be between 0.0f and 1.0f.
        /// </summary>
        /// <param name="motors">The values for the motors. X is for low frequency motor and Y is the high frequency motor.</param>
        public void SetVibration(Vector2 motors)
        {
            SetVibration(motors.X, motors.Y);
        }
    }
}
